package com.zhuangjie.order.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhaungjie.base.conduit.TypeR;
import com.zhaungjie.base.utils.*;
import com.zhaungjie.base.utils.LambdaStructureParser.LambdaStructureParser;
import com.zhaungjie.base.utils.PropertiesCopyUtils.PropertiesCopy;
import com.zhuangjie.base.entity.TimeRange;
import com.zhuangjie.base.vo.PageVO;
import com.zhuangjie.coupon.dto.LockCouponsDTO;
import com.zhuangjie.coupon.dto.QueryConfirmRequireDTO;
import com.zhuangjie.coupon.pojo.CouponCoreInfo;
import com.zhuangjie.coupon.vo.CouponCoreInfoAndPreferentialAmount;
import com.zhuangjie.coupon.vo.UseCouponCoreInfoContainer;
import com.zhuangjie.idworker.helper.SnowflakeHelper;
import com.zhuangjie.mvc.enhance.mp_query_enhance.LambdaQueryWrapperPlus;
import com.zhuangjie.mvc.enhance.mp_query_enhance.LambdaQueryWrapperPlusHelper;
import com.zhuangjie.mvc.enhance.mp_query_enhance.utils.LambdaGetHashMap;
import com.zhuangjie.mvc.future.ContextAwareExecutor;
import com.zhuangjie.mvc.global_lock.GlobalLock;
import com.zhuangjie.mvc.helper.GeoHelper.GeoCoordinateUtils;
import com.zhuangjie.order.constant.OrderConstant;
import com.zhuangjie.order.data.OrderStateCount;
import com.zhuangjie.order.dto.*;
import com.zhuangjie.order.entity.*;
import com.zhuangjie.order.enums.ImmediateEnum;
import com.zhuangjie.order.enums.OrderPaymentState;
import com.zhuangjie.order.enums.OrderState;
import com.zhuangjie.order.enums.PayMethod;
import com.zhuangjie.order.feign.CouponFeignService;
import com.zhuangjie.order.feign.RiderFeignService;
import com.zhuangjie.order.feign.ShopFeignService;
import com.zhuangjie.order.feign.UserFeignService;
import com.zhuangjie.order.lock.OrderLock;
import com.zhuangjie.order.mapper.OrderMapper;
import com.zhuangjie.order.mq.producer.event.OrderEvent;
import com.zhuangjie.order.mq.producer.structure.OrderMQStructure;
import com.zhuangjie.order.pojo.OrderDBTableInfo;
import com.zhuangjie.order.pojo.RiderLocation;
import com.zhuangjie.order.service.*;
import com.zhuangjie.order.vo.*;
import com.zhuangjie.rabbit.send.wrap.MQHelper;
import com.zhuangjie.redis.helper.RedisGeoHelper;
import com.zhuangjie.rider.constant.RiderGeoInfoConstant;
import com.zhuangjie.rider.dto.CreateDeliveryOrderDTO;
import com.zhuangjie.rider.utils.DeliveryCostCalculator;
import com.zhuangjie.rider.vo.DeliveryOrderForUserOrderMapVO;
import com.zhuangjie.security.provider.UserProvider;
import com.zhuangjie.shop.entity.CartDishFlavor;
import com.zhuangjie.shop.entity.Shop;
import com.zhuangjie.shop.enums.BusinessTypeEnum;
import com.zhuangjie.shop.enums.DeliveryMethodEnum;
import com.zhuangjie.shop.helper.ShopModuleTokenDataProvider;
import com.zhuangjie.shop.vo.CartItemVO;
import com.zhuangjie.shop.vo.PredetermineDayAndTimeRangesForInternalUse;
import com.zhuangjie.shop.vo.ShopForUserOrderMapVO;
import com.zhuangjie.shop.vo.order_confirm_require.OrderConfirmRequireVO;
import com.zhuangjie.shop.vo.order_confirm_require.PreferentialInfo;
import com.zhuangjie.user.helper.UserModuleTokenDataProvider;
import com.zhuangjie.user.vo.AddressBookVO;
import io.seata.spring.annotation.GlobalTransactional;
import io.seata.tm.api.GlobalTransactionContext;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Point;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author zhuangjie
 * @since 2023-11-24
 */
@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
    @Autowired
    private ShopFeignService shopFeignService;

    @Autowired
    private SnowflakeHelper snowflakeHelper;

    @Autowired
    private BigDecimalUtils bigDecimalUtils;

    @Autowired
    private UserFeignService userFeignService;

    @Autowired
    private DeliveryInfoService deliveryInfoService;

    @Autowired
    private OrderDetailService orderDetailService;

    @Autowired
    private Redisson redisson;

    @Autowired
    private MQHelper mqHelper;

    @Autowired
    private ShopSSEService shopSSEService;

    @Autowired
    private RiderFeignService riderFeignService;

    @Autowired
    private ContextAwareExecutor contextAwareExecutor;

    @Autowired
    private OrderActivityService orderActivityService;

    @Autowired
    private OrderCouponService orderCouponService;

    @Autowired
    private OrderAmountService orderAmountService;

    @Autowired
    private OrderForSearchService orderForSearchService;

    @Autowired
    private CouponFeignService couponFeignService;

    @Autowired
    private UserSSEService userSSEService;

    @Autowired
    private RedisGeoHelper redisGeoHelper;

    @Override
    @GlobalTransactional
    public CreateOrderCompleteVO createOrder(SubmitOrderDTO submitOrderDTO) throws Exception {
        // 入参检查与娇正
        CompletableFuture<LegalSubmitOrderDTO> legalSubmitOrderDTOFuture = CompletableFuture.supplyAsync(() -> {
            try {
                return submitOrderParamsCheckAndCorrect(submitOrderDTO);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }, contextAwareExecutor);
        // 进行优惠信息查询
        CompletableFuture<OrderConfirmRequireVO> orderConfirmRequireFuture = CompletableFuture.supplyAsync(() -> {
            try {
                QueryConfirmRequireDTO queryConfirmRequireDTO = PropertiesCopy.type(SubmitOrderDTO.class, QueryConfirmRequireDTO.class).produce(submitOrderDTO);
                queryConfirmRequireDTO.setIsReturnCouponDetail(true); // 设置请求返回需要获取详细优惠券信息
                queryConfirmRequireDTO.setIsReturnShopInfo(true); // 需要店铺信息（其顺便返回的）
                return TypeR.extract(shopFeignService.queryOrderConfirmRequire(queryConfirmRequireDTO));
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }, contextAwareExecutor);
        CompletableFuture.allOf(legalSubmitOrderDTOFuture, orderConfirmRequireFuture);
        LegalSubmitOrderDTO legalSubmitOrderDTO = legalSubmitOrderDTOFuture.get();
        OrderConfirmRequireVO orderConfirmRequireVO = orderConfirmRequireFuture.get();
        // 数据检查（订单的菜品是否满足最低要求且不为空）
        orderInfoCheck(orderConfirmRequireVO);
        // 订单相关信息保存到数据库
        OrderDBTableInfo orderDBTableInfo = orderInfoSaveToDB(legalSubmitOrderDTO, orderConfirmRequireVO);
        Order order = orderDBTableInfo.getOrder();
        // MQ延时关闭订单
        mqHelper.send(OrderMQStructure.EXCHANGE_NAME, OrderEvent.ORDER_CANCEL, order.getId());
        // 清理店铺的购物车（下面需要使用自定义线程池，不然主线程结束，会中断）
        CompletableFuture.runAsync(() -> shopFeignService.clearShoppingCart(order.getShopId()), contextAwareExecutor);
        // step4: 组装回调
        return PropertiesCopy.type(Order.class, CreateOrderCompleteVO.class)
                .config(c -> c.map(Order::getId, CreateOrderCompleteVO::getOrderId).map(Order::getAmount, CreateOrderCompleteVO::getPaymentAmount))
                .produce(order);
    }

    /**
     * 订单信息检查
     * 如检查菜品列表是否为空是否符合最低价格
     * @param orderConfirmRequireVO
     */
    private void orderInfoCheck(OrderConfirmRequireVO orderConfirmRequireVO) {
        Shop shopInfo = orderConfirmRequireVO.getShopInfo();
        if (CollectionUtils.isEmpty(orderConfirmRequireVO.getCartItemList())) {
            throw new RuntimeException("订单菜品列表不能为空");
        }
        BigDecimal dishListSumPrice = orderConfirmRequireVO.getDishListSumPrice() == null? BigDecimal.ZERO: orderConfirmRequireVO.getDishListSumPrice();
        if ( dishListSumPrice.compareTo(shopInfo.getMinimumDeliveryAmount()) < 0) {
            throw new RuntimeException("订单菜品总额不满足起购价格");
        }
    }

    private LegalSubmitOrderDTO submitOrderParamsCheckAndCorrect(SubmitOrderDTO submitOrderDTO) throws Exception {
        // 检查业务的必要参数
        checkRequiredParameters(submitOrderDTO);
        // 保证业务参数的合理性
        LegalSubmitOrderDTO legalSubmitOrderDTO = enforceValidBusinessParameters(submitOrderDTO);
        return legalSubmitOrderDTO;
    }

    /**
     * 订单相关数据保存到数据库
     * - order
     * - order_detail
     * - delivery_info
     * - order_activity
     * - order_coupon
     * - order_amount
     * - order-for-search
     *
     * @param legalSubmitOrderDTO
     * @param orderConfirmRequireVO
     * @return
     * @throws Exception
     */
    private OrderDBTableInfo orderInfoSaveToDB(LegalSubmitOrderDTO legalSubmitOrderDTO, OrderConfirmRequireVO orderConfirmRequireVO) throws Exception {
        // 1. 最开始要先构建出order，然后插入到数据库，然后有了orderId，这是其它表需要的
        Order order = buildAndSaveOrder(legalSubmitOrderDTO, orderConfirmRequireVO);
        // 2. 构建非主订单表表信息
        CompletableFuture.allOf(
                CompletableFuture.runAsync(() -> {
                    // - delivery_info 保存用户配送信息到单独的表-保留起来
                    if (BusinessTypeEnum.DELIVERY.getValue().equals(legalSubmitOrderDTO.getReceivingMethod())) {
                        try {
                            deliveryInfoService.saveDeliveryInfo(order.getId(), legalSubmitOrderDTO.getAddressBookId());
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    }
                }, contextAwareExecutor),
                // - order_detail
                CompletableFuture.runAsync(() -> {
                    // step3: 插入菜品,orderId插入成功了order对象就有id了
                    try {
                        // 菜品检查在上层外层已经对orderConfirmRequireVO检查了
                        saveOrderDishInfo(orderConfirmRequireVO.getCartItemList(), order.getId());
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }, contextAwareExecutor),
                // - order_activity
                CompletableFuture.runAsync(() -> {
                    saveOrderActivity(orderConfirmRequireVO.getPreferentialInfo().getAvailableActivityList(), order.getId());
                }, contextAwareExecutor),
                // - order_coupon
                CompletableFuture.runAsync(() -> {
                    saveOrderCoupon(orderConfirmRequireVO.getPreferentialInfo().getUsedCoupons(), order.getId());
                }, contextAwareExecutor),
                // - order_amount
                CompletableFuture.runAsync(() -> {
                    try {
                        saveOrderAmount(legalSubmitOrderDTO.getReceivingMethod(), orderConfirmRequireVO, order);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }, contextAwareExecutor),
                // - order_for_search
                CompletableFuture.runAsync(() -> {
                    saveOrderForSearch(orderConfirmRequireVO, order);
                }, contextAwareExecutor),
                // 锁定优惠券
                CompletableFuture.runAsync(() -> {
                    UseCouponCoreInfoContainer usedCoupons = orderConfirmRequireVO.getPreferentialInfo().getUsedCoupons();
                    if (usedCoupons != null) {
                        List<CouponCoreInfoAndPreferentialAmount> fuse = usedCoupons.fuse();
                        if (fuse == null || fuse.isEmpty()) return;
                        List<Long> couponIds = fuse.stream().map(couponCoreInfoAndPreferentialAmount -> couponCoreInfoAndPreferentialAmount.getCouponCoreInfo().getId()).collect(Collectors.toList());
                        LockCouponsDTO lockCouponsDTO = new LockCouponsDTO();
                        lockCouponsDTO.setOrderId(order.getId());
                        lockCouponsDTO.setCouponIds(couponIds);
                        // 开启系统访问内部接口
                        // moduleSecurityHelper.useModuleSecurityAccess();
                        // 开始调用远程内部接口
                        try {
                            TypeR.capture(couponFeignService.lockCoupons(lockCouponsDTO));
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    }
                }, contextAwareExecutor)
        ).join();


        return OrderDBTableInfo.builder()
                .order(order)
                .build();

    }

    /**
     * 保存订单将用于搜索的信息
     *
     * @param orderConfirmRequireVO
     */
    private void saveOrderForSearch(OrderConfirmRequireVO orderConfirmRequireVO, Order order) {
        Shop shopInfo = orderConfirmRequireVO.getShopInfo();

        OrderForSearch orderForSearch = new OrderForSearch();
        orderForSearch.setOrderId(order.getId());
        orderForSearch.setShopName(shopInfo.getName());
        orderForSearch.setDishes(getDishNamesStringify(orderConfirmRequireVO.getCartItemList()));

        orderForSearchService.save(orderForSearch);
    }

    /**
     * 保存订单金额详细信息
     *
     * @param receivingMethod       接收方法
     * @param orderConfirmRequireVO 订单确认要求vo
     * @param order                 顺序
     * @throws Exception 例外
     */
    private void saveOrderAmount(String receivingMethod, OrderConfirmRequireVO orderConfirmRequireVO, Order order) throws Exception {
        PreferentialInfo preferentialInfo = orderConfirmRequireVO.getPreferentialInfo();
        OrderAmount orderAmount = new OrderAmount();
        orderAmount.setOrderId(order.getId());
        orderAmount.setTotalDishAmount(orderConfirmRequireVO.getDishListSumPrice());
        if (Objects.equals(receivingMethod, BusinessTypeEnum.DELIVERY.getValue())) {
            orderAmount.setPresetDeliveryFee(orderConfirmRequireVO.getDeliveryFee());
            // 如果是配送单 && 是平台配送，那就添加实际配送费
            if (Objects.equals(DeliveryMethodEnum.PLATFORM.getValue(), order.getDeliveryMethod())) {
                orderAmount.setActualDeliveryFee(DeliveryCostCalculator.calculateDeliveryCost(calculateShopUserDistance(orderConfirmRequireVO)));
            }
        }
        if (Objects.equals(receivingMethod, BusinessTypeEnum.SELF_PICKUP.getValue()) || Objects.equals(receivingMethod, BusinessTypeEnum.DELIVERY.getValue())) {
            orderAmount.setPackingFee(orderConfirmRequireVO.getPackingFee());
        }
        orderAmount.setShopActivityDiscountAmount(preferentialInfo.getActivityDeductionAmount());
        orderAmount.setShopCouponDiscountAmount(preferentialInfo.getAllShopCouponDeductionAmount());
        orderAmount.setPlatformCouponDiscountAmount(preferentialInfo.getAllPlatformCouponDeductionAmount());
        orderAmount.setShopBornePlatformCouponDiscountAmount(preferentialInfo.calculateShopBearingPlatformCouponDeductionAmount());
        orderAmount.setUserPaymentAmount(preferentialInfo.getActivityAndCouponPreferentialAfterOrderAmount());
        orderAmount.calculateAndSetDynamicProperties();
        orderAmountService.save(orderAmount);
    }

    /**
     * 计算店铺与用户的距离
     *
     * @param orderConfirmRequireVO
     * @return
     */
    private double calculateShopUserDistance(OrderConfirmRequireVO orderConfirmRequireVO) {
        AddressBookVO addressBook = orderConfirmRequireVO.getAddressBook();
        Shop shopInfo = orderConfirmRequireVO.getShopInfo();
        if (addressBook == null || shopInfo == null) {
            throw new RuntimeException("无法计算店铺到用户的距离");
        }
        return GeoCoordinateUtils.calculateDistance(addressBook.getLongitude(), addressBook.getLatitude(), shopInfo.getLongitude(), shopInfo.getLatitude());
    }

    /**
     * 保存订单使用的优惠券
     *
     * @param usedCoupons
     * @param orderId
     */
    private void saveOrderCoupon(UseCouponCoreInfoContainer usedCoupons, Long orderId) {
        if (usedCoupons == null || usedCoupons.isAllEmpty()) return;
        List<CouponCoreInfoAndPreferentialAmount> fuse = usedCoupons.fuse();
        List<OrderCoupon> orderCouponList = new ArrayList<>();
        for (CouponCoreInfoAndPreferentialAmount couponCoreInfoAndPreferentialAmount : fuse) {
            CouponCoreInfo couponCoreInfo = couponCoreInfoAndPreferentialAmount.getCouponCoreInfo();
            OrderCoupon orderCoupon = new OrderCoupon();
            orderCoupon.setOrderId(orderId);
            orderCoupon.setCouponId(couponCoreInfo.getId());
            orderCoupon.setCouponName(couponCoreInfo.getName());
            orderCoupon.setShopBearingAmount(couponCoreInfo.getExpansionAmount());
            orderCoupon.setDeductionAmount(couponCoreInfoAndPreferentialAmount.getPreferentialAmount());
            orderCouponList.add(orderCoupon);
        }
        orderCouponService.saveBatch(orderCouponList);
    }

    /**
     * 保存订单使用的活动
     *
     * @param availableActivityList
     * @param orderId
     */
    private void saveOrderActivity(List<OrderActivity> availableActivityList, Long orderId) {
        if (availableActivityList == null || availableActivityList.isEmpty()) return;
        availableActivityList.forEach(orderActivity -> orderActivity.setOrderId(orderId));
        this.orderActivityService.saveBatch(availableActivityList);
    }


    /**
     * 与cancelOrder有区别，它不需要加userId来判断
     *
     * @param orderId
     * @param cancelReason
     * @throws Exception
     */
    @Override
    @GlobalTransactional
    public void cancelOrderForMQ(Long orderId, String cancelReason) throws Exception {
        try {
            updateOrderStateTo(orderId, OrderState.ORDER_CANCELED, updateWrapper -> {
                updateWrapper
                        .eq(Order::getState, OrderState.WAITING_FOR_PAYMENT)
                        .set(Order::getCancelReason, cancelReason);
            });
        } catch (Exception e) {
            throw new Exception("MQ取消订单失败！");
        }
    }


    @Override
    @GlobalTransactional
    public void paymentOrder(Long orderId) {
        // ... 假设这里已经判断支付的确实是orderId已经支付
        updateOrderStateTo(orderId, OrderState.WAITING_FOR_ACCEPTANCE, updateWrapper -> {
            updateWrapper
                    .eq(Order::getState, OrderState.WAITING_FOR_PAYMENT)
                    .set(Order::getPayMethod, PayMethod.WEIXIN_PAY)
                    .set(Order::getPayTime, LocalDateTime.now());
        });
        // 通知商家-来单了
        Order order = this.getById(orderId);
        shopSSEService.newOrderArrived(order);
    }

    @Override
    public OrderInfoVO getOrderInfo(Long orderId) {
        // 获取订单
        Order order = this.getById(orderId);
        OrderInfoVO orderInfoVO = PropertiesCopy.type(Order.class, OrderInfoVO.class)
                .produce(order);
        // 获取店家联系电话
        CompletableFuture<String> getShopPhoneFuture = CompletableFuture.supplyAsync(() ->
                TypeR.tryExtract(shopFeignService.getShopPhone(order.getShopId())));
        // 获取订单详细
        CompletableFuture<List<OrderDetail>> getOrderDetailFuture = CompletableFuture.supplyAsync(() ->
                orderDetailService.list(Wrappers.lambdaQuery(OrderDetail.class).eq(OrderDetail::getOrderId, orderId)));
        // 获取订单配送地址
        CompletableFuture<String> getDeliveryAddressFuture = CompletableFuture.supplyAsync(() -> {
            if (!BusinessTypeEnum.DELIVERY.getValue().equals(order.getReceivingMethod())) return null;
            DeliveryInfo deliveryInfo = deliveryInfoService.getOne(Wrappers.lambdaQuery(DeliveryInfo.class).eq(DeliveryInfo::getOrderId, orderId));
            return deliveryInfo.getDetailAddress() + " " + deliveryInfo.getSpecificAddress();
        });
        // 等待所有异步结果
        CompletableFuture.allOf(getShopPhoneFuture, getOrderDetailFuture, getDeliveryAddressFuture);
        // 数据组装
        orderInfoVO.setOrderDetailList(getOrderDetailFuture.join());
        orderInfoVO.setShopPhone(getShopPhoneFuture.join());
        orderInfoVO.setDeliveryAddress(getDeliveryAddressFuture.join());
        return orderInfoVO;
    }

    @Override
    public ShopOrderPageVO listShopOrder(ShopOrderPageDTO shopOrderPageDTO) {
        Long shopId = ShopModuleTokenDataProvider.getShopId();

        // 获取店铺订单-分页数据
        Page<ShopOrderVO> orderPage = getShopOrderVOPage(shopOrderPageDTO, shopId);
        // 查询一些状态（除了终点状态“完成”、“取消“）的计数
        Map<Integer, Integer> statesCountMap = queryStatesCount(shopId, Arrays.asList(OrderState.ORDER_COMPLETED, OrderState.ORDER_CANCELED));

        // 组装返回值
        ShopOrderPageVO shopOrderPageVO = new ShopOrderPageVO();
        // - 组装分页数据
        shopOrderPageVO.setTotalPage((int) orderPage.getTotal());
        shopOrderPageVO.setList(orderPage.getRecords());
        // - 组装状态统计数
        shopOrderPageVO.setStateCountMap(statesCountMap);

        return shopOrderPageVO;
    }

    @Override
    @GlobalTransactional
    public void shopRejectOrder(ShopRejectOrderDTO shopRejectOrderDTO) throws Exception {
        Long shopId = ShopModuleTokenDataProvider.getShopId();
        try {
            updateOrderStateTo(shopRejectOrderDTO.getOrderId(), OrderState.ORDER_CANCELED, updateWrapper -> {
                updateWrapper
                        .eq(Order::getShopId, shopId)
                        .eq(Order::getState, OrderState.WAITING_FOR_ACCEPTANCE)
                        .set(Order::getRejectionReason, shopRejectOrderDTO.getReason());
            });
        } catch (Exception e) {
            throw new RuntimeException("修改失败，状态可能已经改变！");
        }
        // TODO 为用户退款
        // 通知用户订单已经被商家拒单(退款通知)
        mqHelper.send(OrderMQStructure.EXCHANGE_NAME, OrderEvent.ORDER_REFUND, shopRejectOrderDTO.getOrderId());
    }

    @Override
    @GlobalTransactional
    public void shopReceivingOrder(Long orderId) {
        Long shopId = ShopModuleTokenDataProvider.getShopId();
        try {
            updateOrderStateTo(orderId, OrderState.ORDER_ACCEPTED, updateWrapper -> {
                updateWrapper
                        .eq(Order::getShopId, shopId)
                        .eq(Order::getState, OrderState.WAITING_FOR_ACCEPTANCE);
            });
            // 判断订单类型，如果Order是配送单，创建配送单DeliveryOrder
            LambdaQueryWrapper<Order> queryOrderReceivingMethod = Wrappers.lambdaQuery(Order.class)
                    .eq(Order::getId, orderId)
                    .select(Order::getReceivingMethod, Order::getUserId);
            // 要使用order的值，看上面是否查询列~~~
            Order order = this.getOne(queryOrderReceivingMethod);
            CompletableFuture.allOf(
                    CompletableFuture.runAsync(() -> {
                        // 配送单 && 查询order_amount是否有实际配送金额不为空，那就需要创建配送单
                        if (BusinessTypeEnum.DELIVERY.getValue().equals(order.getReceivingMethod())) {
                            BigDecimal orderActualDeliveryFee = orderAmountService.getOrderActualDeliveryFee(orderId);
                            if (orderActualDeliveryFee != null) {
                                // 创建配送单
                                // 异步获取用户配送信息与店铺位置信息
                                CreateDeliveryOrderDTO createDeliveryOrderDTO = new CreateDeliveryOrderDTO();
                                createDeliveryOrderDTO.setOrderId(orderId);
                                createDeliveryOrderDTO.setActualDeliveryFee(orderActualDeliveryFee);

                                try {
                                    TypeR.capture(riderFeignService.submit(createDeliveryOrderDTO));
                                } catch (Exception e) {
                                    throw new RuntimeException("创建配送订单失败！");
                                }
                            }
                        }
                    }, contextAwareExecutor),
                    CompletableFuture.runAsync(() -> userSSEService.orderStateChange(order.getUserId(), orderId))
            ).join();
        } catch (Exception e) {
            throw new RuntimeException("接单失败，请重试！");
        }
    }

    @Override
    @GlobalTransactional
    public void productionCompleted(Long orderId) {
        Long shopId = ShopModuleTokenDataProvider.getShopId();
        LambdaQueryWrapper<Order> queryOrderWrapper = Wrappers.lambdaQuery(Order.class)
                .eq(Order::getId, orderId)
                .eq(Order::getShopId, shopId);
        Order order = this.getOne(queryOrderWrapper);
        String receivingMethod = order.getReceivingMethod();
        Integer targetState = OrderState.PRODUCTION_COMPLETED;
        // 如果是店食单，端餐后就已经完成了
        if (BusinessTypeEnum.SHOP_EAT.getValue().equals(receivingMethod)) {
            targetState = OrderState.ORDER_COMPLETED;
        }
        try {
            updateOrderStateTo(orderId, targetState, updateWrapper -> {
                updateWrapper
                        .eq(Order::getShopId, shopId)
                        .eq(Order::getState, OrderState.ORDER_ACCEPTED);
            });
        } catch (Exception e) {
            throw new RuntimeException("操作失败，订单状态可能已经改变！");
        }


    }

    @Override
    public PageVO<OrderForUserVO> getOrderListForUser(OrderListForUserPageDTO orderListForUserPageDTO) throws Exception {
        Long currentUserId = UserModuleTokenDataProvider.getCurrentUserId();
        // 构造分页
        Page<OrderForUserVO> orderForUserVOPage = this.baseMapper.selectOrderPageForUser(currentUserId
                , new Page<>(orderListForUserPageDTO.getCurrentPage(), orderListForUserPageDTO.getPageSize())
                , orderListForUserPageDTO);
        List<OrderForUserVO> records = orderForUserVOPage.getRecords();

        if (records != null && !records.isEmpty()) {
            // 查询出这些订单的菜品详情
            CompletableFuture<Map<Long, List<OrderDetail>>> orderIdAndOrderDetailMapFuture = CompletableFuture.supplyAsync(() -> {
                List<Long> orderIds = records.stream().map(OrderForUserVO::getId).collect(Collectors.toList());
                LambdaQueryWrapper<OrderDetail> queryOrderDetailWrapper = Wrappers.lambdaQuery(OrderDetail.class)
                        .in(OrderDetail::getOrderId, orderIds);
                List<OrderDetail> orderDetailList = orderDetailService.list(queryOrderDetailWrapper);
                Map<Long, List<OrderDetail>> orderIdAndOrderDetailMap = orderDetailList.stream()
                        .collect(Collectors.groupingBy(OrderDetail::getOrderId));
                return orderIdAndOrderDetailMap;
            });
            // 获取店铺的信息组装到订单信息中
            CompletableFuture<Map<Long, ShopInfoForOrder>> shopIdAndShopInfoMapFuture = CompletableFuture.supplyAsync(() -> {
                String shopIdsStringify = shopIdsToStringify(records, OrderForUserVO::getShopId);
                try {
                    List<ShopInfoForOrder> shopInfo = TypeR.extract(shopFeignService.getShopInfoListForOrder(shopIdsStringify));
                    Map<Long, ShopInfoForOrder> shopIdAndShopInfoMap = shopInfo.stream().collect(Collectors.toMap(ShopInfoForOrder::getShopId, Function.identity()));
                    return shopIdAndShopInfoMap;
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }, contextAwareExecutor);
            CompletableFuture.allOf(orderIdAndOrderDetailMapFuture, shopIdAndShopInfoMapFuture);
            Map<Long, List<OrderDetail>> orderIdAndOrderDetailMap = orderIdAndOrderDetailMapFuture.get();
            Map<Long, ShopInfoForOrder> shopIdAndShopInfoMap = shopIdAndShopInfoMapFuture.get();
            // 组装到OrderForUserVO
            records.forEach(order -> {
                // 将菜品详情组装到对应的OrderForUser对象中
                order.setDishList(orderIdAndOrderDetailMap.get(order.getId()));
                // 将店铺信息给到OrderForUserVO对象中
                PropertiesCopy.type(ShopInfoForOrder.class, OrderForUserVO.class)
                        .setTemplate(order)
                        .produce(shopIdAndShopInfoMap.get(order.getShopId()));
            });
        }
        return new PageVO<>(orderForUserVOPage);
    }


    private <T> String shopIdsToStringify(List<T> records, LambdaStructureParser.NAHRFunction<T, ?> shopIdFunction) {
        String fieldName = LambdaStructureParser.convertToFieldName(shopIdFunction);
        return records.stream().map(order -> String.valueOf(ReflectionUtils.getFieldValue(order, fieldName))).distinct().collect(Collectors.joining(","));
    }


    @Override
    public List<OrderDetailForEvaluate> listDisheForEvaluateListByOrderId(Long orderId) {
        Long currentUserId = UserModuleTokenDataProvider.getCurrentUserId();
        // 从订单表中查看当前用户是否存在该订单
        long targetOrderCount = this.baseMapper.selectCount(Wrappers.lambdaQuery(Order.class)
                .eq(Order::getUserId, currentUserId)
                .eq(Order::getId, orderId));
        if (targetOrderCount != 1) throw new RuntimeException("订单id无效!");
        // 获取该订单基本的订单详情
        return this.orderDetailService.getOrderDetailForEvaluateListByOrderId(orderId);
    }

    @Override
    public OrderDetailForDeliveryOrderDetailVO orderDetailForDeliveryOrderDetail(Long orderId) {
        List<OrderDetail> orderDetailForEvaluateVOS = this.listDishesByOrderId(orderId);
        Order order = this.getById(orderId);
        OrderDetailForDeliveryOrderDetailVO orderDetailForDeliveryOrderDetailVO = PropertiesCopy.type(Order.class, OrderDetailForDeliveryOrderDetailVO.class)
                .produce(order);
        // 菜品列表-赋值
        orderDetailForDeliveryOrderDetailVO.setDishList(orderDetailForEvaluateVOS);
        return orderDetailForDeliveryOrderDetailVO;
    }

    @Override
    @GlobalTransactional
    public void updateDeliveryOrderToDelivering(Long orderId) {
        try {
            updateOrderStateTo(orderId, OrderState.IN_DELIVERY, updateWrapper -> {
                updateWrapper
                        .eq(Order::getReceivingMethod, BusinessTypeEnum.DELIVERY.getValue())
                        .between(Order::getState, OrderState.ORDER_ACCEPTED, OrderState.PRODUCTION_COMPLETED);
            });
        } catch (Exception e) {
            throw new RuntimeException("更新状态为派送中失败，状态可能已经改变！");
        }
    }

    @Override
    @GlobalTransactional
    public void cancelOrder(Long orderId, String cancelReason) throws Exception {
        try {
            this.updateOrderStateTo(orderId, OrderState.ORDER_CANCELED, updateWrapper -> {
                updateWrapper
                        .eq(Order::getUserId, UserProvider.currentUserId())
                        .eq(Order::getState, OrderState.WAITING_FOR_PAYMENT)
                        .set(Order::getCancelReason, cancelReason);
            });
        } catch (Exception e) {
            throw new Exception("取消订单失败！");
        }
    }

    /**
     * 更新订单状态到指定状态（要求必须通过这个来进行-会有一些后置处理器）
     * （可自行添加修改条件）
     *
     * @param orderId
     * @param targetState
     * @param updateWrapperConsumer
     */
    // @GlobalTransactional(propagation = Propagation.REQUIRED) 在这里标注不会生效，因为出现“方法内调用”
    @Override
    public void updateOrderStateTo(Long orderId, Integer targetState, Consumer<LambdaUpdateWrapper<Order>> updateWrapperConsumer) {
        RLock lock = redisson.getLock(OrderLock.getOrderOperationLock(orderId));
        lock.lock();
        try {
            System.out.println("updateOrderStateTo2=" + GlobalTransactionContext.getCurrentOrCreate().getXid());

            LambdaUpdateWrapper<Order> updateDeliveryOrderToDeliveringWrapper = Wrappers.lambdaUpdate(Order.class)
                    .eq(Order::getId, orderId)
                    .set(Order::getState, targetState);
            updateWrapperConsumer.accept(updateDeliveryOrderToDeliveringWrapper);
            System.out.println("updateOrderStateTo3=" + GlobalTransactionContext.getCurrentOrCreate().getXid());

            boolean isUpdateSuccess = this.update(updateDeliveryOrderToDeliveringWrapper);
            if (!isUpdateSuccess) {
                throw new RuntimeException("更新配送订单为已完成失败，状态可能已经改变！");
            }
            // 如果将订单取消，那需要将订单的优惠券状态设置为未使用
            if (targetState == OrderState.ORDER_CANCELED) {
                returnOrderCoupons(orderId);
            } else if (targetState == OrderState.ORDER_COMPLETED) {
                // 给商家或骑手进行打账操作
                orderFinishedPostProcess(orderId);
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * 订单完成后置处理器
     *
     * @param orderId
     */
    private void orderFinishedPostProcess(Long orderId) {
        // 下面涉及到远程调用，需要开启模块安全访问（token标记）
        // moduleSecurityHelper.useModuleSecurityAccess();
        // 当订单完成时，需要给商家/骑手进账操作
        CompletableFuture.allOf(
                CompletableFuture.runAsync(() -> {
                    // 给商家进行打账（是延时到账）
                    try {
                        TypeR.capture(shopFeignService.createOrderAccounting(orderId));
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }, contextAwareExecutor),
                CompletableFuture.runAsync(() -> {
                    // 给骑手进行打账（是延时到账）
                    // - 查看订单是否为平台配送订单
                    if (!this.isDeliveryOrderAndPlatformDelivery(orderId)) return;
                    try {
                        TypeR.capture(riderFeignService.createOrderAccounting(orderId));
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }, contextAwareExecutor)
        ).join();
    }

    /**
     * 判断是否为配送单且该订单是否为平台配送
     *
     * @return
     */
    private boolean isDeliveryOrderAndPlatformDelivery(Long orderId) {
        return this.count(Wrappers.lambdaQuery(Order.class)
                .eq(Order::getId, orderId)
                .eq(Order::getReceivingMethod, BusinessTypeEnum.DELIVERY.getValue())
                .eq(Order::getDeliveryMethod, DeliveryMethodEnum.PLATFORM.getValue())) > 0;
    }

    /**
     * 退还订单使用的优惠券
     *
     * @param orderId
     */
    public void returnOrderCoupons(Long orderId) {
        System.out.println("returnOrderCouponsXid=" + GlobalTransactionContext.getCurrentOrCreate().getXid());

        // 尝试获取订单使用的优惠券
        List<OrderCoupon> orderCouponList = orderCouponService.list(Wrappers.lambdaQuery(OrderCoupon.class)
                .eq(OrderCoupon::getOrderId, orderId));
        if (orderCouponList == null || orderCouponList.isEmpty()) return;
        // 发送远程请求取消锁定退还优惠券
        LockCouponsDTO lockCouponsDTO = new LockCouponsDTO();
        lockCouponsDTO.setOrderId(orderId);
        lockCouponsDTO.setCouponIds(orderCouponList.stream().map(OrderCoupon::getCouponId).collect(Collectors.toList()));
        // 准备调用系统内部接口，所以在这里需要开启安全访问
        // moduleSecurityHelper.useModuleSecurityAccess();
        if (TypeR.isFailed(couponFeignService.unLockCoupons(lockCouponsDTO))) {
            throw new RuntimeException("取消锁定退还优惠券失败！");
        }
    }

    @Override
    @GlobalTransactional
    public void updateDeliveryOrderToCompleted(Long orderId) throws Exception {
        updateOrderStateTo(orderId, OrderState.ORDER_COMPLETED, updateWrapper -> {
            updateWrapper
                    .eq(Order::getReceivingMethod, BusinessTypeEnum.DELIVERY.getValue())
                    .eq(Order::getState, OrderState.IN_DELIVERY);
        });
    }

    @Override
    public Boolean isNewCustomer(Long shopId, Long userId) {
        // 是否在店铺有正常订单
        long isNormalOrderInShop = this.count(Wrappers.lambdaQuery(Order.class)
                .eq(Order::getShopId, shopId)
                .eq(Order::getUserId, userId)
                .between(Order::getState, OrderState.WAITING_FOR_ACCEPTANCE, OrderState.APPLY_FOR_AFTER_SALE));
        return isNormalOrderInShop == 0;
    }

    private List<OrderDetail> listDishesByOrderId(Long orderId) {
        return this.orderDetailService.getOrderDetailByOrderId(orderId);
    }


    /**
     * 获取店铺订单-分页数据
     *
     * @param shopOrderPageDTO 车间订单页面dto
     * @param shopId           店铺id
     * @return {@link Page}<{@link ShopOrderVO}>
     */
    private Page<ShopOrderVO> getShopOrderVOPage(ShopOrderPageDTO shopOrderPageDTO, Long shopId) {
        Page<ShopOrderVO> orderPage = this.baseMapper.selectShopOrder(new Page<>(shopOrderPageDTO.getCurrentPage(), shopOrderPageDTO.getPageSize()), shopId, shopOrderPageDTO);
        List<ShopOrderVO> records = orderPage.getRecords();
        // 设置records中每个ShopOrderVO的dishSummary属性
        if (records != null && !records.isEmpty()) {
            // 查订单的菜品信息
            List<Long> orderIds = records.stream().map(ShopOrderVO::getId).collect(Collectors.toList());
            LambdaQueryWrapper<OrderDetail> queryOrdersDishDetailWrapper = Wrappers.lambdaQuery(OrderDetail.class)
                    .in(OrderDetail::getOrderId, orderIds)
                    .select(OrderDetail::getOrderId, OrderDetail::getName, OrderDetail::getDishFlavor, OrderDetail::getCount);

            List<OrderDetail> orderDetails = orderDetailService.list(queryOrdersDishDetailWrapper);
            // 将orderDetails分配给各ShopOrderVO中属性
            Map<Long, List<OrderDetail>> orderIdAndOrderDetailMap = new HashMap<>();
            for (OrderDetail orderDetail : orderDetails) {
                List<OrderDetail> inMapOrderDetails = orderIdAndOrderDetailMap.get(orderDetail.getOrderId());
                if (inMapOrderDetails == null) {
                    orderIdAndOrderDetailMap.put(orderDetail.getOrderId(), inMapOrderDetails = new ArrayList<>());
                }
                inMapOrderDetails.add(orderDetail);
            }
            for (ShopOrderVO shopOrderVO : records) {
                // 将每个ShopOrderVO的List<OrderDetail> 转为`菜品一（规格）*2,菜品二（规格）*3` 这种设置到ShopOrderVO的dishSummary属性中
                List<OrderDetail> orderDetailList = orderIdAndOrderDetailMap.get(shopOrderVO.getId());
                if (orderDetailList == null || orderDetailList.isEmpty()) {
                    shopOrderVO.setDishSummary("");
                } else {
                    shopOrderVO.setDishSummary(orderDetailList.stream().map(_orderDetail -> _orderDetail.getName() + (_orderDetail.getDishFlavor() == null ? "" : "(" + _orderDetail.getDishFlavor() + ")") + "*" + _orderDetail.getCount()).collect(Collectors.joining("; ")));
                }
                // 配送方式-代号转意
                shopOrderVO.setReceivingMethodName(BusinessTypeEnum.nameOf(shopOrderVO.getReceivingMethod()));
            }
        }
        return orderPage;
    }

    /**
     * 查询订单状态计数
     *
     * @param shopId
     * @param excludeStateList 排除状态列表
     * @return {@link Map}<{@link Integer}, {@link Integer}>
     */
    private Map<Integer, Integer> queryStatesCount(Long shopId, List<Integer> excludeStateList) {
        List<OrderStateCount> stateCounts = this.baseMapper.selectStatesCount(shopId, excludeStateList);
        Map<Integer, Integer> statesCountMap;
        if (stateCounts != null || !stateCounts.isEmpty()) {
            statesCountMap = stateCounts.stream().collect(Collectors.toMap(OrderStateCount::getState, OrderStateCount::getCount));
        } else {
            statesCountMap = new HashMap<>();
        }
        return statesCountMap;
    }

//    private Order tryGetDuplicateUnpaidOrdersAndResetTimeout(List<CartItemVO> cartItemList) {
//        String orderKey  = genOrderKey(cartItemList);
//        if (StringUtils.isEmpty(orderKey)) {
//            // 如果生成的orderKey是无效（""），则表示没有
//            return null;
//        }
//        // 查询用户未支付订单看是否有相同订单
//        LambdaQueryWrapper<Order> queryDuplicateUnpaidOrders = Wrappers.lambdaQuery(Order.class)
//                .eq(Order::getOrderKey, orderKey);
//        Order order = this.getBaseMapper().selectOne(queryDuplicateUnpaidOrders);
//        if (order == null) {
//            // 没有找到
//            return null;
//        }
//        RLock lock =
//        .getLock(OrderLock.getOrderOperationLock(order.getId()));
//        lock.lock();
//        try {
//            // 重置订单超时时间
//            LambdaUpdateWrapper<Order> resetOrderTimeoutWrapper = Wrappers.lambdaUpdate(Order.class)
//                    .eq(Order::getId, order.getId())
//                    // 是未支付状态
//                    .eq(Order::getPayStatus, OrderPaymentState.UNPAID)
//                    .set(Order::getPayTimeoutTime, LocalDateTime.now().plusMinutes(OrderConstant.PAYMENT_TIMEOUT));
//            boolean isResetSuccess = this.update(resetOrderTimeoutWrapper);
//            // 如果重置订单超时时间失败，返回空，此时同时候异步的订单关闭修改此条记录 / 支付订单操作（除非多人登录了此账号）
//            if (!isResetSuccess) return null;
//        }finally {
//            lock.unlock();
//        }
//        log.info("使用已存在未支付相同订单");
//        return order;
//    }

    /**
     * 保存点菜信息
     *
     * @param cartItemVOList 购物车项目列表
     * @param orderId        订单id
     * @throws Exception 例外
     */
    private void saveOrderDishInfo(List<CartItemVO> cartItemVOList, Long orderId) throws Exception {
        // 购物车信息转OrderDetail
        List<OrderDetail> orderDetailList = cartItemVOList.stream().map(cartItemVO -> {
            OrderDetail orderDetail = PropertiesCopy.type(CartItemVO.class, OrderDetail.class)
                    .config(c -> c.map(CartItemVO::getId, OrderDetail::getDishId).valueAdapter(CartItemVO::getId, CartItemVO::genDishSpecificationStr))
                    .produce(cartItemVO);
            // 每一项菜品都需要绑定订单号
            orderDetail.setOrderId(orderId);
            // 设置菜品口味(字符串形式)
            orderDetail.setDishFlavor(getFlavorStringify(cartItemVO.getFlavors()));
            return orderDetail;
        }).collect(Collectors.toList());
        // 批量保存
        orderDetailService.saveBatch(orderDetailList);
    }

    /**
     * 获取风味字符串
     *
     * @param flavors 风味
     * @return {@link String}
     */
    private String getFlavorStringify(List<CartDishFlavor> flavors) {
        if (flavors == null || flavors.isEmpty()) return null;
        return flavors.stream().map(flavor -> flavor.getName()).collect(Collectors.joining(","));
    }


    /**
     * 构建并保存订单
     *
     * @param legalSubmitOrderDTO   合法提交订单DTO
     * @param orderConfirmRequireVO 订单确认要求VO
     * @return {@link Order}
     * @throws Exception 例外
     */
    private Order buildAndSaveOrder(LegalSubmitOrderDTO legalSubmitOrderDTO, OrderConfirmRequireVO orderConfirmRequireVO) throws Exception {
        Long userId = UserProvider.currentUserId();
        Order order = new Order();
        order.setOrderNo(String.valueOf(snowflakeHelper.nextId()));
        order.setShopId(legalSubmitOrderDTO.getShopId());
        order.setUserId(userId);
        order.setState(OrderState.WAITING_FOR_PAYMENT);
        order.setPayStatus(OrderPaymentState.UNPAID);
        order.setPayTimeoutTime(LocalDateTime.now().plusMinutes(OrderConstant.PAYMENT_TIMEOUT));
        order.setRemark(legalSubmitOrderDTO.getRemark());
        order.setDesignatedDeliveryTime(legalSubmitOrderDTO.isImmediate() ? null : legalSubmitOrderDTO.getExpectedDeliveryTime());
        order.setDeliveryDuration(legalSubmitOrderDTO.getDeliveryDuration());
        order.setEstimatedDeliveryTime(legalSubmitOrderDTO.getExpectedDeliveryTime());
        order.setReceivingMethod(legalSubmitOrderDTO.getReceivingMethod());
        order.setDeliveryMethod(orderConfirmRequireVO.getShopInfo().getDeliveryMethod());
        // 设置特有属性
        if (BusinessTypeEnum.SHOP_EAT.getValue().equals(legalSubmitOrderDTO.getReceivingMethod())) {
            order.setCode(legalSubmitOrderDTO.getSeatNumber());
        } else {
            // 获取-设置店铺"#N"编号（配送-自提都有）
            Integer selfPickupNumber = TypeR.extract(shopFeignService.nextShopSelfPickupSerialNumber(legalSubmitOrderDTO.getShopId()));
            order.setCode(String.valueOf(selfPickupNumber));
        }
        order.setAmount(orderConfirmRequireVO.getPreferentialInfo().getActivityAndCouponPreferentialAfterOrderAmount());
        // 保存order对象(保存成功后order对象中就有插入那条记录的id了)
        this.save(order);
        return order;
    }

    /**
     * 获取购物车中菜名集合的stringify
     *
     * @param cartItemList 购物车项目列表
     * @return {@link String}
     */
    private String getDishNamesStringify(List<CartItemVO> cartItemList) {
        return cartItemList.stream().map(CartItemVO::getName).collect(Collectors.joining(","));
    }


    /**
     * 生成订单key（菜品相同Key相同）
     *
     * @param cartItemList 购物车项目列表
     * @return {@link String}
     */
    private String genOrderKey(List<CartItemVO> cartItemList) {
        if (cartItemList == null || cartItemList.isEmpty()) return "";
        String rawKey = cartItemList.stream().map(CartItemVO::genDishSpecificationKey).collect(Collectors.joining());
        return MD5Utils.md5(rawKey);
    }

    /**
     * 强制执行有效业务参数
     *
     * @param submitOrderDTO 提交订单dto
     */
    private LegalSubmitOrderDTO enforceValidBusinessParameters(SubmitOrderDTO submitOrderDTO) throws Exception {
        LegalSubmitOrderDTO legalSubmitOrderDTO = PropertiesCopy.type(SubmitOrderDTO.class, LegalSubmitOrderDTO.class)
                .produce(submitOrderDTO);
        // 获取预定列表信息-后端自己设定时间
        PredetermineDayAndTimeRangesForInternalUse predetermineDayAndTimeRangesForInternalUse = TypeR.extract(shopFeignService.shopBusinessDaysTimeRangesForInternalUse(submitOrderDTO.getShopId(), submitOrderDTO.getReceivingMethod(), submitOrderDTO.getAddressBookId()));
        // 如果isImmediate为空，需要保留为1
        if (submitOrderDTO.getIsImmediate() == null) submitOrderDTO.setIsImmediate(1);
        // 维护相关参数，保证合理
        if (Objects.equals(submitOrderDTO.getIsImmediate(), ImmediateEnum.IMMEDIATE.getCode())) {
            // 获取指定店铺的立即送达时间 用于设置值
            legalSubmitOrderDTO.setExpectedDeliveryTime(predetermineDayAndTimeRangesForInternalUse.getImmediatePredetermineDateTime());
        } else {
            // title: 确保指定时间是安全的
            // 设置指定时间（已校检）就是预定交付时间
            legalSubmitOrderDTO.setExpectedDeliveryTime(submitOrderDTO.getSpecifiedTime());
            // core: 确保指定时间是安全的
            ensureSafeExpectedTime(predetermineDayAndTimeRangesForInternalUse, submitOrderDTO.getSpecifiedTime());
        }
        // 设置需要配送时长
        if (Objects.equals(BusinessTypeEnum.DELIVERY.getValue(), legalSubmitOrderDTO.getReceivingMethod())) {
            legalSubmitOrderDTO.setDeliveryDuration(predetermineDayAndTimeRangesForInternalUse.getDeliveryTime() + predetermineDayAndTimeRangesForInternalUse.getDeliveryTime());
        }
        return legalSubmitOrderDTO;
    }

    /**
     * 确保指定时间是安全的
     *
     * @param predetermineDayAndTimeRangesForInternalUse 预先确定内部使用日期和时间范围
     * @param rawSpecifiedDateTime                       原始指定日期时间
     */
    private void ensureSafeExpectedTime(PredetermineDayAndTimeRangesForInternalUse predetermineDayAndTimeRangesForInternalUse, LocalDateTime rawSpecifiedDateTime) {
        List<List<TimeRange>> predetermineDaysValidPeriod = predetermineDayAndTimeRangesForInternalUse.getPredetermineDaysValidPeriod();
        Integer makeTime = predetermineDayAndTimeRangesForInternalUse.getMakeTime();
        Integer deliveryTime = predetermineDayAndTimeRangesForInternalUse.getDeliveryTime();
        if (deliveryTime == null) deliveryTime = 0;
        // 由基础时间+makeTime+deliveryTime -> 基础时间+makeTime
        LocalDateTime specifiedDateTime = rawSpecifiedDateTime.minusMinutes(deliveryTime);
        // rawSpecifiedDateTime的“基础时间+makeTime”的LocalTimeOperations对象
        LocalTimeOperations specifiedDateTimeOperations = new LocalTimeOperations(specifiedDateTime.toLocalTime());

        // 检查参数的合理性
        if (specifiedDateTime == null || predetermineDaysValidPeriod == null || predetermineDaysValidPeriod.isEmpty())
            throw new RuntimeException("无法维护 指定的时间！");
        // title: 锁定指定的日期那天
        // need: 求出当前与预定的那天相差的天数 (第一localDate 要小于 第二个localDate方得正数)
        int dateIndexOfSpecifiedTime = (int) ChronoUnit.DAYS.between(LocalDate.now(), specifiedDateTime.toLocalDate());
        // core: 获取预定的那天的可预定时间段
        List<TimeRange> timeRanges = predetermineDaysValidPeriod.get(dateIndexOfSpecifiedTime);
        if (timeRanges == null || timeRanges.isEmpty()) throw new RuntimeException("指定的时间无效！");
        // 判断是否specifiedTime 的time部分是否有效
        for (TimeRange timeRange : timeRanges) {
            String startTime = timeRange.getStartTime();
            // startTime + makeTime > specifiedTime的time部分
            LocalTimeOperations startLocalTimeOperations = new LocalTimeOperations(startTime);
            // startTime + makeTime
            LocalTimeOperations timeRangeFirstOrderTime = new LocalTimeOperations(startTime).addMinute(makeTime);
            LocalTimeOperations specifiedTimeCopy = specifiedDateTimeOperations.copyInstance();
            if (specifiedTimeCopy.toNumberValue() > startLocalTimeOperations.toNumberValue()) {
                // 如果是介于 startTimeOperations与timeRangeFirstOrderTime之间，则对specifiedTimeCopy进行调整
                if (timeRangeFirstOrderTime.toNumberValue() < specifiedTimeCopy.toNumberValue()) {
                    specifiedTimeCopy.getCore().withHour(timeRangeFirstOrderTime.getCore().getHour());
                    specifiedTimeCopy.getCore().withMinute(timeRangeFirstOrderTime.getCore().getMinute());
                }
            } else {
                continue;
            }
            // 来到这里说明specifiedTime满足startTime，接下来看specifiedTime是否满足endTime
            // 要满足endTime，只需要看 specifiedTime <= endTime 即可
            LocalTimeOperations endLocalTimeOperations = new LocalTimeOperations(timeRange.getEndTime());

            if (specifiedTimeCopy.toNumberValue() > endLocalTimeOperations.toNumberValue()) {
                // specifiedTime不满足endTime
                continue;
            }
            // 到这里表示找到了，证明了specifiedTime是合理的,应将可能被调用的specifiedDateTime的time部分覆盖到specifiedDateTime
            // - 基础时间+makeTime -> 基础时间+makeTime+deliveryTime
            specifiedTimeCopy.addMinute(deliveryTime);
            LocalTime specifiedTimeCopyLocalTime = specifiedTimeCopy.getCore();
            rawSpecifiedDateTime.withHour(specifiedTimeCopyLocalTime.getHour()).withMinute(specifiedTimeCopyLocalTime.getMinute());
            return;
        }
        throw new RuntimeException("指定的时间校验无效！");
    }


    /**
     * 验证业务参数
     *
     * @param submitOrderDTO 提交订单dto
     */
    private void checkRequiredParameters(SubmitOrderDTO submitOrderDTO) {
        if (BusinessTypeEnum.SHOP_EAT.getValue().equals(submitOrderDTO.getReceivingMethod())) {
            // 如果是店食，那需要有座位号
            if (submitOrderDTO.getSeatNumber() == null) throw new RuntimeException("未绑定座位号");
        } else {
            // 如果是自提与配送，isImmediate肯定要有值
            if (submitOrderDTO.getIsImmediate() == null) throw new RuntimeException("自提与配送，isImmediate肯定要有值");
            if (submitOrderDTO.getIsImmediate() == 0 && submitOrderDTO.getSpecifiedTime() == null)
                throw new RuntimeException("非立即订单需要指定时间！");
            if (BusinessTypeEnum.DELIVERY.getValue().equals(submitOrderDTO.getReceivingMethod()) && submitOrderDTO.getAddressBookId() == null)
                throw new RuntimeException("配送单必须指定用户收货地址！");
        }
    }


    /**
     * 查询单个订单为了用户地图
     * 注意：如果修改查询条件，你可能需要修改一下this::listForMapShow,因为两者是密切关联的
     *
     * @param orderId
     * @return
     * @throws Exception
     */
    @Override
    public OrderForMapShow getOneMapShowOrderInfo(Long orderId) throws Exception {
        // 1. 查询主要信息
        OrderForMapShow orderForMapShow = this.baseMapper.selectOneMapShowOrderInfo(UserProvider.currentUserId(), orderId,
                BusinessTypeEnum.SELF_PICKUP.getValue(),
                BusinessTypeEnum.DELIVERY.getValue(),
                OrderState.WAITING_FOR_ACCEPTANCE,
                OrderState.IN_DELIVERY);

        if (orderForMapShow == null) return null;
        CompletableFuture.allOf(
                CompletableFuture.runAsync(() -> {
                    // 2. 查询订单对应的店铺信息
                    ShopForUserOrderMapVO shopForUserOrderMapVO = null;
                    try {
                        shopForUserOrderMapVO = TypeR.extract(shopFeignService.selectShopInfoForUserOrderMap(orderForMapShow.getShopId()));
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                    // 3. shopForUserOrderMapVO -> orderForMapShow
                    orderForMapShow.setShopForUserOrderMap(shopForUserOrderMapVO);
                }, contextAwareExecutor),
                CompletableFuture.runAsync(() -> {
                    if (orderForMapShow.getReceivingMethod().equals(BusinessTypeEnum.DELIVERY.getValue()) && orderForMapShow.getState().compareTo(OrderState.WAITING_FOR_ACCEPTANCE) > 0) {
                        // 查询配送订单状态
                        try {
                            DeliveryOrderForUserOrderMapVO deliveryOrderForUserOrderMapVO = TypeR.extract(riderFeignService.deliveryInfoForUserOrderMap(orderForMapShow.getId()));
                            orderForMapShow.setDeliveryOrderForUserOrderMap(deliveryOrderForUserOrderMapVO);
                            // 查询骑手位置
                            RiderLocation riderLocation = getRiderLocation(deliveryOrderForUserOrderMapVO.getRiderId());
                            orderForMapShow.setRiderLocation(riderLocation);
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    }
                }, contextAwareExecutor),
                CompletableFuture.runAsync(() -> {
                    // 查看未完成的自提与配送订单数量
                    orderForMapShow.setOrderCount((int) this.count(getUserMapOrderListQueryWrapper()));
                }, contextAwareExecutor)
        ).join();

        return orderForMapShow;
    }

    /**
     * 获取用户地图展示订单列表QueryWrapper
     *
     * @return {@link LambdaQueryWrapper}<{@link Order}>
     */
    public LambdaQueryWrapper<Order> getUserMapOrderListQueryWrapper() {
        return Wrappers.lambdaQuery(Order.class)
                .eq(Order::getUserId, UserProvider.currentUserId())
                .ne(Order::getReceivingMethod, BusinessTypeEnum.SHOP_EAT.getValue())
                .between(Order::getState, OrderState.WAITING_FOR_ACCEPTANCE, OrderState.IN_DELIVERY);

    }

    /**
     * 查询可地图展示的订单
     * 注意：如果修改查询条件，你可能需要修改一下this::getOneMapShowOrderInfo,因为两者是密切关联的
     *
     * @return
     */
    @Override
    public List<OrderForUserMapVO> listForMapShow() {
        List<Order> orderList = this.list(getUserMapOrderListQueryWrapper());

        if (orderList == null || orderList.isEmpty()) return new ArrayList<>();
        List<OrderForUserMapVO> userMapOrderList = PropertiesCopy.type(Order.class, OrderForUserMapVO.class)
                .config(c -> c.map(Order::getId, OrderForUserMapVO::getOrderId))
                .batchProduce(orderList);
        // 查询用户信息
        try {
            List<ShopInfoForOrder> shopInfoForOrderList = TypeR.extract(shopFeignService.getShopInfoListForOrder(shopIdsToStringify(orderList, Order::getShopId)));
            if (shopInfoForOrderList == null || shopInfoForOrderList.isEmpty())
                throw new RuntimeException("获取店铺信息失败");
            // 关联拷贝
            PropertiesCopy.type(ShopInfoForOrder.class, OrderForUserMapVO.class)
                    .associated(ShopInfoForOrder::getShopId, OrderForUserMapVO::getShopId)
                    .copy(shopInfoForOrderList, userMapOrderList);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return userMapOrderList;
    }

    @Override
    public OrderForUserEvaluateVO getOrderForUserEvaluate(Long orderId) {

        // 查询订单信息（仅需要的）
        OrderForUserEvaluateVO orderForUserEvaluateVO = LambdaQueryWrapperPlusHelper
                .queryOne(new LambdaQueryWrapperPlus<>(Order.class, OrderForUserEvaluateVO.class)
                        .eq(Order::getUserId, UserProvider.currentUserId())
                        .eq(Order::getId, orderId), this::getOne);
        // 查询OrderForUserEvaluateVO需要的其它信息
        CompletableFuture.allOf(
                CompletableFuture.runAsync(() -> {
                    try {
                        String shopIds = shopIdsToStringify(Collections.singletonList(orderForUserEvaluateVO), OrderForUserEvaluateVO::getShopId);
                        List<ShopInfoForOrder> shopInfos = TypeR.extract(shopFeignService.getShopInfoListForOrder(shopIds));
                        if (shopInfos == null || shopInfos.isEmpty()) throw new RuntimeException();
                        ShopInfoForOrder shopInfoForOrder = shopInfos.get(0);
                        PropertiesCopy.type(ShopInfoForOrder.class, OrderForUserEvaluateVO.class)
                                .setTemplate(orderForUserEvaluateVO)
                                .produce(shopInfoForOrder);
                    } catch (Exception e) {
                        throw new RuntimeException("在为评价查询信息时，无法获取店铺信息！");
                    }

                }, contextAwareExecutor),
                CompletableFuture.runAsync(() -> {
                    try {
                        List<OrderDetailForEvaluate> orderDetailForEvaluateList = LambdaQueryWrapperPlusHelper
                                .queryList(new LambdaQueryWrapperPlus<>(OrderDetail.class, OrderDetailForEvaluate.class)
                                        .eq(OrderDetail::getOrderId, orderForUserEvaluateVO.getId()));
                        orderForUserEvaluateVO.setOrderDetailList(orderDetailForEvaluateList);
                    } catch (Exception e) {
                        throw new RuntimeException("在为评价查询信息时，订单的菜品列表信息！");
                    }
                }, contextAwareExecutor)
        ).join();
        return orderForUserEvaluateVO;
    }

    @Override
    @GlobalTransactional
    @GlobalLock(instanceKey = {"#orderForUserEvaluateVO.orderId"})
    public void submitOrderEvaluate(OrderEvaluateSubmitDTO orderForUserEvaluateVO) {
        // 订单的取餐方式
        AtomicReference<String> orderReceivingMethod = new AtomicReference<>();
        AtomicReference<Long> orderShopId = new AtomicReference<>();
        // 查看订单信息，只能完成的订单才能进行评价
        LambdaQueryWrapperPlusHelper.forUse(
                        new LambdaQueryWrapperPlus<>(Order.class)
                                .eq(Order::getUserId, UserProvider.currentUserId())
                                .eq(Order::getId, orderForUserEvaluateVO.getOrderId())
                                .eq(Order::getState, OrderState.ORDER_COMPLETED))
                .use(Order::getReceivingMethod, orderReceivingMethod::set)
                .lastUse(Order::getShopId, orderShopId::set);

        if (orderReceivingMethod.get() == null || orderShopId.get() == null) {
            throw new RuntimeException("无法确认订单取餐方式|订单店铺id，所以无法评价！");
        }
        // 将参数颁发到对应的模块完成评价请求（骑手与商家）
        CompletableFuture.allOf(
                CompletableFuture.runAsync(() -> {
                    try {
                        // 需要向子请求DTO中传递orderId
                        orderForUserEvaluateVO.getShopEvaluate().setOrderId((orderForUserEvaluateVO.getOrderId()));
                        orderForUserEvaluateVO.getShopEvaluate().setShopId(orderShopId.get());
                        // 颁发请求
                        TypeR.capture(shopFeignService.submitShopEvaluate(orderForUserEvaluateVO.getShopEvaluate()));
                    } catch (Exception e) {
                        throw new RuntimeException("评价店铺失败！" + e.getMessage());
                    }
                }, contextAwareExecutor),
                CompletableFuture.runAsync(() -> {
                    try {
                        // 只能订单配送时，才需要评价骑手
                        if (!Objects.equals(orderReceivingMethod.get(), BusinessTypeEnum.DELIVERY.getValue())) return;
                        // 需要向子请求DTO中传递orderId
                        orderForUserEvaluateVO.getRiderEvaluate().setOrderId((orderForUserEvaluateVO.getOrderId()));

                        // 颁发请求
                        TypeR.capture(riderFeignService.submitRiderEvaluate(orderForUserEvaluateVO.getRiderEvaluate()));
                    } catch (Exception e) {
                        throw new RuntimeException("评价骑手失败！" + e.getMessage());
                    }
                }, contextAwareExecutor)
        ).join();

        // 改变订单状态为已评价
        updateOrderStateTo(orderForUserEvaluateVO.getOrderId(), OrderState.ORDER_EVALUATED, updateWrapper -> {
            // 订单必须是当前用户的
            updateWrapper.eq(Order::getUserId, UserProvider.currentUserId());
            // 订单状态必须是已完成才能评价
            updateWrapper.eq(Order::getState, OrderState.ORDER_COMPLETED);
        });
    }


    @Override
    public Map<Long, Long> monthSalesCount(Collection<Long> shopIds, Integer recentDays) {
        Map<LambdaGetHashMap<Object>, Long> map = LambdaQueryWrapperPlusHelper.groupCount(new LambdaQueryWrapperPlus<>(Order.class)
                .in(Order::getShopId, shopIds)
                .gt(Order::getPayTime, LocalDateTime.now().minusDays(recentDays))
                .groupBy(Order::getShopId));
        return map.entrySet().stream()
                .collect(Collectors
                        .toMap(entry -> Long.valueOf(String.valueOf(entry.getKey().get(Order::getShopId))), Map.Entry::getValue));

    }

    /**
     * 获取骑手位置
     *
     * @param riderId
     * @return
     */
    private RiderLocation getRiderLocation(Long riderId) {
        Point riderPoint = redisGeoHelper.get(RiderGeoInfoConstant.RIDER_GEO_INFO_KEY, String.valueOf(riderId));
        if (riderPoint == null) return null;
        RiderLocation riderLocation = new RiderLocation();
        riderLocation.setRiderId(riderId);
        riderLocation.setLongitude(riderPoint.getX());
        riderLocation.setLatitude(riderPoint.getY());
        return riderLocation;
    }
}
